/*
 *only support for "exact division"
 *
 *
 * */



#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "2d5p_aceMesh.h"
#include "task.h"
#include "dma.h"

#define alpha_d 0.0876
#define beta_d  0.0765

__thread_local volatile unsigned long get_reply,put_reply;


//core region
__thread_local_fix double slave[2][BLKX][BLKY];
//__thread_local double A_lhs[BLKX][BLKY];
//__thread_local double A_rhs[BLKX][BLKY];
//up and down shade region
__thread_local double A_us[BLKY];
__thread_local double A_ds[BLKY];
//left and right shade  region
__thread_local double A_ls[BLKX];
__thread_local double A_rs[BLKX];
//Record the logical information of the last time executed task.
__thread_local int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;
//__thread_local int last_iter,last_starti,last_startj;
__thread_local dma_desc dma_d;

#ifdef _PROFILING
__thread_local volatile unsigned long start,end;
__thread_local volatile unsigned long dma_start1,dma_end1,dma_start2,dma_end2;
__thread_local volatile unsigned long comp_start1,comp_end1;
__thread_local int core_id=0;
__thread_local int myreuse=0;
extern unsigned long slave_comp_count[64];
extern unsigned long slave_dma_count[64];
extern unsigned long slave_count[64];
extern unsigned long slave_reuse_count[64];
extern unsigned long slave_up[64];
extern unsigned long slave_up1[64]; //only halo
extern unsigned long slave_down[64];

unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

extern double A[2][SIZEX][SIZEY];

void stencil_core(targs* args)
{
   int bsize,stride,tsize; 
   int tmp[8],*buf;
   double tmpij,tmpij1,tmpij2,tmpij3;

   int tileno[2];
   int varno[2],reuse[2];
   int lhs_no,rhs_no;
   int dma_get_count=0;
   int dma_put_count=0;

   buf=&(tmp[0]);
   dma_start1=dma_end1=dma_start2=dma_end2=0;
   simd_load(*((intv8*)buf),args);
   s_iter=tmp[4],s_starti=tmp[0],s_endi=tmp[1],s_startj=tmp[2],s_endj=tmp[3];
   dest = (s_iter) % 2 ; 
   src = 1 - dest;
                   
   start=rtc_();
   core_id=athread_get_id(-1);

   tileno[0]=s_starti/BLKX;
   tileno[1]=s_startj/BLKY;
   slave_ldm_postreq_tile(&A[dest][0][0], 2,tileno, OUT);
   slave_ldm_postreq_tile(&A[src][0][0], 2, tileno, IN);
   slave_ldm_getreq_tile(varno,reuse);
   lhs_no = varno[0];
   rhs_no = varno[1];
  
#ifdef _PROFILING
   slave_up[core_id]+=2*(BLKX+BLKY);
   slave_down[core_id]+=BLKX*BLKY;
   slave_up1[core_id]+=2*BLKX;
   dma_start1=rtc_();
#endif

   get_reply = 0;
   if(rhs_no <0)
      printf("Array assignment failed\n");
   else if(reuse[1]==0)
   {
     //1.0 update inner-core data
     stride = (SIZEY-BLKY)*8;
     tsize = BLKX*BLKY*8;
     bsize = BLKY*8;
     athread_get(PE_MODE
          ,&A[src][s_starti][s_startj], &slave[rhs_no][0][0]
          , tsize, (void*)&get_reply
          , 0, stride, bsize);
     dma_get_count++;
   }
#ifdef _PROFILING
   else
     myreuse++;
#endif
   //1.1 update up and down shade data    
   athread_get(PE_MODE,&A[src][s_starti-1][s_startj],&A_us[0],BLKY*8,(void*)&get_reply,0,0,0); 
   athread_get(PE_MODE,&A[src][s_endi][s_startj], &A_ds[0],BLKY*8,(void*)&get_reply,0,0,0);
   //1.2 update left and right shade data
   stride = (SIZEY-1)*8;
   tsize = BLKX*8;
   bsize = 8;
   athread_get(PE_MODE,&A[src][s_starti][s_startj-1], &A_ls[0], tsize,(void*)&get_reply, 0, stride, bsize);
   athread_get(PE_MODE,&A[src][s_starti][s_endj], &A_rs[0], tsize,(void*)&get_reply, 0, stride, bsize);  
   dma_get_count+=4;
   while(get_reply != dma_get_count); 
   
#ifdef _PROFILING
      dma_end1=rtc_();
      slave_dma_count[core_id]+=((dma_end1-dma_start1));
      comp_start1=rtc_();
#endif
   for(i = 1; i < BLKX-1; i++)
     for(j = 1; j < BLKY-1; j+=4)
     {
       tmpij=slave[rhs_no][i][j];
       tmpij1=slave[rhs_no][i][j+1];
       tmpij2=slave[rhs_no][i][j+2];
       tmpij3=slave[rhs_no][i][j+3];
       slave[lhs_no][i][j] = alpha_d * (tmpij) + \
                     beta_d * (slave[rhs_no][i-1][j] + slave[rhs_no][i+1][j] +\
                     slave[rhs_no][i][j-1] + tmpij1);
       slave[lhs_no][i][j+1] = alpha_d * (tmpij1) + \
                     beta_d * (slave[rhs_no][i-1][j+1] + slave[rhs_no][i+1][j+1] +\
                     tmpij + tmpij2);
       slave[lhs_no][i][j+2] = alpha_d * (tmpij2) + \
                     beta_d * (slave[rhs_no][i-1][j+2] + slave[rhs_no][i+1][j+2] +\
                      tmpij1+ tmpij3);
       slave[lhs_no][i][j+3] = alpha_d * (tmpij3) + \
                     beta_d * (slave[rhs_no][i-1][j+3] + slave[rhs_no][i+1][j+3] +\
                     tmpij2 + slave[rhs_no][i][j+3+1]);
     }
   slave[lhs_no][0][0] = alpha_d * (slave[rhs_no][0][0]) + \
                  beta_d * (A_us[0] + slave[rhs_no][1][0] +\
                  A_ls[0] + slave[rhs_no][0][1]);
   slave[lhs_no][BLKX-1][0] =   alpha_d * (slave[rhs_no][BLKX-1][0]) +\
                        beta_d * (slave[rhs_no][BLKX-2][0] + A_ds[0] +\
                        A_ls[BLKX-1] + slave[rhs_no][BLKX-1][1]);
   slave[lhs_no][0][BLKY-1] = alpha_d * (slave[rhs_no][0][BLKY-1]) + \
                      beta_d * (A_us[BLKY-1] +\
                      slave[rhs_no][1][BLKY-1] +\
                      slave[rhs_no][0][BLKX-2] + A_rs[0]);
   slave[lhs_no][BLKX-1][BLKY-1] = alpha_d * \
                           (slave[rhs_no][BLKX-1][BLKY-1]) + \
                           beta_d * (slave[rhs_no][BLKX-2][BLKY-1] + \
                           A_ds[BLKY-1] + \
                           slave[rhs_no][BLKX-1][BLKY-2] + \
                           A_rs[BLKX-1]);
//#pragma ivdep
   for(j = 1;j < BLKY-1; ++j)
   {
     slave[lhs_no][0][j] = alpha_d * (slave[rhs_no][0][j]) + \
                   beta_d * (A_us[j] + slave[rhs_no][1][j] +\
                   slave[rhs_no][0][j-1] + slave[rhs_no][0][j+1]);
     slave[lhs_no][BLKX-1][j] = alpha_d * (slave[rhs_no][BLKX-1][j]) + \
                   beta_d * (slave[rhs_no][BLKX-2][j] + A_ds[j] +\
                   slave[rhs_no][BLKX-1][j-1] + slave[rhs_no][BLKX-1][j+1]);
   }
   for(i = 1;i < BLKX-1; ++i)
   {
     slave[lhs_no][i][0] = alpha_d * (slave[rhs_no][i][0]) + \
                   beta_d * (slave[rhs_no][i-1][0] + slave[rhs_no][i+1][0] +\
                   A_ls[i] + slave[rhs_no][i][1]);
     slave[lhs_no][i][BLKY-1] = alpha_d * (slave[rhs_no][i][BLKY-1]) + \
                   beta_d * (slave[rhs_no][i-1][BLKY-1] + slave[rhs_no][i+1][BLKY-1] +\
                   slave[rhs_no][i][BLKY-2] + A_rs[i]);
   }
#ifdef _PROFILING
      comp_end1=rtc_();
      slave_comp_count[core_id]+=(comp_end1-comp_start1);
      dma_start2=rtc_();
#endif

   //write back
   put_reply=0;
   bsize=(BLKY)*8;
   stride=(SIZEY-(BLKY))*8;  //stride: from blocktail to blockhead
   tsize= bsize*(BLKX);
   athread_put(PE_MODE,&slave[lhs_no][0][0]
          , &A[dest][s_starti][s_startj]
          ,tsize,(void*)&put_reply,stride,bsize);
   while(put_reply!=1);
#ifdef _PROFILING
    dma_end2=rtc_();
    slave_dma_count[core_id]+=((dma_end2-dma_start2));
    slave_reuse_count[core_id]=myreuse;
#endif
}
